/**
 * This file is a part of qloudgen-sec. 
 * You can redistribute qloudgen-sec and/or modify it under the terms of the Lesser GNU General Public License version 3. 
 * qloudgen-sec is distributed WITHOUT ANY WARRANTY. 
 *
 * See the Lesser GNU General Public License for more details at http://www.gnu.org/licenses/. 
 **/
package org.qloudgen.sec.shiro.realm;

import java.util.Arrays;
import java.util.Collection;

import io.buji.pac4j.ClientRealm;
import org.pac4j.cas.profile.CasProfile;

import org.apache.shiro.util.Destroyable;
import org.apache.shiro.authz.Permission;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.subject.PrincipalCollection;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.qloudgen.rul.drools.Message;
import org.qloudgen.rul.drools.rule.DroolsRuleRunner;
import org.qloudgen.rul.drools.rule.DroolsRuleReceiver;
import org.qloudgen.rul.curator.CuratorClient;

import org.qloudgen.sec.shiro.permission.DroolsPermission;

/**
 * <p>Title: Cas and Drools Realm Implementation</p>
 * <p>Description: Wrappered shiro realm implementation.</p>
 * <p>Copyright: qloudgen/sec 2014</p>
 * @author <a href="mailto:hyq.dd.sun@gmail.com">glue.3M</a>
 */
@SuppressWarnings( { "serial" , "unchecked" } )
public class DroolsRealm extends ClientRealm implements Destroyable {
	/**
	 * Log output.
	 */
	private final static Logger logger = LoggerFactory.getLogger( DroolsRealm.class );
	/**
	 *
	 */
	private DroolsRuleRunner droolsRuleRunner = null;
	/**
	 *
	 */
	private CuratorClient curatorClient = null;
	
	/**
	 * Default constructor.
	 */
	public DroolsRealm() {
		super();
	}
	
	/**
	 * Override parent method to support current realm init.
	 */
	@Override
	protected void onInit() {
		logger.info( "Starting initialize composite realm..." );
		//
		droolsRuleRunner.init();
		// Optional.
		if ( droolsRuleRunner instanceof DroolsRuleReceiver ) {
			curatorClient.init();
		}
	}
	
	/**
	 * Implements parent method to support current realm destroy.
	 */
	public void destroy() {
		logger.info( "Starting destroy composite realm..." );
		// Optional.
		if ( droolsRuleRunner instanceof DroolsRuleReceiver ) {
			curatorClient.destroy();
		}
		//
		droolsRuleRunner.destroy();
	}
	
	/**
	 * Override parent method to support current realm init.
	 */
	@Override
	protected void afterCacheManagerSet() {
		/**
		 * Do parent method first.
		 */
		super.afterCacheManagerSet();
		//
		logger.info( "Current composite realm cache manager is...{}" , getCacheManager() );
	}
	
	/**
	 * Sets current drool rule runner.
	 *
	 * @param droolsRuleRunner
	 */
	public void setDroolsRuleRunner(final DroolsRuleRunner droolsRuleRunner) {
		this.droolsRuleRunner = droolsRuleRunner;
	}
	
	/**
	 * Returns current drool rule runner.
	 *
	 * @return DroolsRuleRunner
	 */
	public DroolsRuleRunner getDroolsRuleRunner() {
		return droolsRuleRunner;
	}
	
	/**
	 * Sets current curator client.
	 *
	 * @param curatorClient
	 */
	public void setCuratorClient(final CuratorClient curatorClient) {
		this.curatorClient = curatorClient;
	}
	
	/**
	 * Returns current curator client.
	 *
	 * @return CuratorClient
	 */
	public CuratorClient getCuratorClient() {
		return curatorClient;
	}
	
	/**
	 * Authorization implementation.
	 *
	 * @param principals
	 * @return AuthorizationInfo
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(final PrincipalCollection principals) {
		/**
		 * Do initialized roles & string permissions first.
		 */
		SimpleAuthorizationInfo authorizationInfo = ( SimpleAuthorizationInfo )super.doGetAuthorizationInfo( principals );
		/**
		 * Do initialized objects permissions finally.
		 */
		authorizationInfo.addObjectPermissions( getObjectPermissions( principals ) );
		/**
		 * Do return all permissions.
		 */
		return authorizationInfo;
	}
	
	/**
	 * Returns current objects permissions collections.
	 *
	 * @return Collection<Permission>
	 */
	protected Collection<Permission> getObjectPermissions(final PrincipalCollection principals) {
		Permission result = new DroolsPermission( droolsRuleRunner , new Object[]{ getDroolsMessage( principals ) } );
		return Arrays.asList( result );
	}
	
	/**
	 * Returns current drools user message.
	 *
	 * @return Message
	 */
	private Message getDroolsMessage(final PrincipalCollection principals) {
		Message message = new Message();
		CasProfile profile = getUserProfile( principals );
		if ( profile != null ) {
			message.setUserId( profile.getId() );
			message.setUserAttributes( profile.getAttributes() );
		}
		//
		return message;
	}
	
	/**
	 * Returns current cas user profile.
	 *
	 * @return CasProfile
	 */
	private CasProfile getUserProfile(final PrincipalCollection principals) {
		return principals.oneByType( CasProfile.class );
	}
	
	/**
	 * Returns the String representation.
	 *
	 * @return String
	 */
	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder.append( "[" );
		builder.append( super.toString() );
		builder.append( "-->" );
		builder.append( "droolsRuleRunner=" ).append( droolsRuleRunner );
		builder.append( "]" );
		//
		return builder.toString();
	}
}